<?php
/**
 *（Logic）
 */
namespace App\Logic\Amazon\Api;

use App\Models\Amazon\CreateAndGetReport;
use App\Models\Basics\AmazonCountry;
use Illuminate\Support\Facades\DB;
use SellingPartnerApi\Api\ReportsV20210630Api as ReportsApi;
use SellingPartnerApi\Document;
use SellingPartnerApi\Model\ReportsV20210630\CreateReportScheduleSpecification;
use SellingPartnerApi\Model\ReportsV20210630\CreateReportSpecification;
use SellingPartnerApi\Model\ReportsV20210630\Report as ReportsModelApi;
class Report
{
    /**
     * 1.创建请求报告（报告id）
     * @param $config
     * @param $account
     * @param $paramData
     * @param $marketplace_ids
     * @param bool $multi
     * @param int $origin
     * @return bool|array
     */
    public static function apiCreateReport($config, $account, $paramData, $marketplace_ids, bool $multi = false, $origin = 0): bool|array
    {
        if(!$account)return false;
        if(!isset($paramData['report_type']) || !$paramData['report_type'])return false;

        $apiInstance = new ReportsApi($config);
        $re = [];
        DB::connection('mysql_amazon')->beginTransaction();
        try{
            if($multi){
                //同一账号,单个市场请求 (单市场ID报告)
                foreach ($marketplace_ids as $marketplaceId){
                    $paramData['marketplace_ids'] = [$marketplaceId];
                    $reportId = self::_createReport($apiInstance,$paramData); //获取报告id
                    if($reportId){
                        //(报告入库)
                        self::_extractForReportIdByCreateReport($account->id,$reportId,$paramData,$origin,$marketplaceId);
                        $re[$marketplaceId]= $reportId;
                    }
                    sleep(2);
                }
            }else{
                //同一账号,多个市场请求 (多市场ID报告)
                $paramData['marketplace_ids'] = $marketplace_ids;
                $reportId = self::_createReport($apiInstance,$paramData);
                if($reportId){
                    //(报告入库)
                    self::_extractForReportIdByCreateReport($account->id,$reportId,$paramData,$origin);
                    $re[implode('-',$marketplace_ids)] = $reportId;
                }
                sleep(2);
            }
            DB::connection('mysql_amazon')->commit();
        }catch (\Exception $e){
            DB::connection('mysql_amazon')->rollBack();
            report($e);
        }
        return $re;
    }

    /**
     * 报告数据录入(入库)
     * @param $accountId
     * @param $reportId
     * @param $paramData
     * @param int $origin
     * @param null $marketplaceId
     */
    private static function _extractForReportIdByCreateReport($accountId,$reportId,$paramData,$origin = 0, $marketplaceId = null): void
    {
        $count = CreateAndGetReport::query()->where('report_id',$reportId)->count();
        if($count == 0){
            $res = [];
            $res['account_id'] = $accountId;
            $res['report_id'] = $reportId;
            $res['report_type'] = $paramData['report_type'];
            $marketplaceId && $res['marketplace_id'] = $marketplaceId;
            $res['origin'] = $origin;

            //businessReport
            if ($paramData['report_type'] == 'GET_SALES_AND_TRAFFIC_REPORT') {
                $res['report_date'] = date('Y-m-d', strtotime($paramData['data_start_time']));
            }
            //allOrderReport
            if ($paramData['report_type'] == 'GET_XML_ALL_ORDERS_DATA_BY_ORDER_DATE_GENERAL') {
                $res['report_date'] = date('Y-m-d', strtotime($paramData['data_start_time']));
            }

            CreateAndGetReport::query()->create($res);
        }
    }

    /**
     * 获取报告id
     * @param ReportsApi $apiInstance
     * @param $paramData
     * @return bool|string
     */
    private static function _createReport(ReportsApi $apiInstance,$paramData): bool|string
    {
        $body = new CreateReportSpecification($paramData);
        try{
            $result = $apiInstance->createReport($body);
            return $result->getReportId();
        }catch (\Exception $e){
            report($e);
            return false;
        }
    }

    /**
     * 2.获取报告，（2.1）确认报告处理已完成 + （2.2）下载报告并解析数据
     * @param $config
     * @param $account
     * @param $reportType
     * @param null $date
     * @return bool|array
     */
    public static function apiGetAndParseReport($config,$account,$reportType,$date = null): bool|array{

        $re = [];
        if(!$account) return false;
        $apiInstance = new ReportsApi($config);

        $query = CreateAndGetReport::query();
        if($reportType){
            $query->where('report_type',$reportType['name']);
        }
        if($date){
            $query->where('report_date',$date);
        }
        $query->where('is_schedule',CreateAndGetReport::IS_SCHEDULE_NO); //0: 非计划报告、需要report_id
        $query->where('is_parse',CreateAndGetReport::IS_PARSE_NO); //0: 未解析报告
        $query->where('account_id',$account->id);
        //$query->where('origin',1);
        $query->orderBy('id','ASC');
        $query->chunk(100, function($needs) use($apiInstance,$account,$reportType,$re){
            foreach ($needs as $need){
                //获取报告
                $report = self::_getReport($apiInstance,$need->report_id);
                if ($report) {
                    $processingStatus = $report->getProcessingStatus();
                    $re[$need->report_id] = $processingStatus;
                    try{
                        DB::connection('mysql_amazon')->beginTransaction();
                        $need->data_start_time = gmdate('Y-m-d H:i:s',strtotime($report->getDataStartTime()));
                        $need->data_end_time = gmdate('Y-m-d H:i:s',strtotime($report->getDataEndTime()));
                        $need->created_time = gmdate('Y-m-d H:i:s',strtotime($report->getCreatedTime()));
                        $need->processing_status = $processingStatus;
                        if($processingStatus == 'DONE'){
                            $need->report_document_id = $report->getReportDocumentId();
                        }elseif($processingStatus == 'CANCELLED' || $processingStatus == 'FATAL'){
                            $need->is_parse = 1;//设置为已解析
                            $need->parse_desc = 3;//设置为无需解析
                        }
                        $need->save();

                        //下载解析报告
                        if($processingStatus == 'DONE' && !empty($need->report_document_id)){
                            $lines = self::_downloadDocument($apiInstance,$reportType,$need->report_document_id);
                            $param = [$lines,$account->id];
                            if($need->marketplace_id){
                                $param[] = AmazonCountry::getCountryIdByMarketplaceId($need->marketplace_id); //报告归属国家id (参数)
                            }

                            //解析类（数据模型,表数据）
                            $parseClass = __NAMESPACE__."\\Parse\\".CreateAndGetReport::getParseClass($reportType['name']);
                            $parseStatus = call_user_func_array([$parseClass,'exe'],$param);
                            $need->is_parse = 1; //解析状态 (0待解析 1已解析)
                            $need->parse_desc = $parseStatus; //解析状况 (0未解析 1解析正常 2解析异常 3无需解析)
                            $need->save();
                        }
                        DB::connection('mysql_amazon')->commit();
                    }catch (\Exception $e){
                        DB::connection('mysql_amazon')->rollBack();
                        report($e);
                    }
                }
                sleep(2);
            }
        });

        return $re;
    }

    private static function _getReport(ReportsApi $apiInstance,$reportId): bool|array|ReportsModelApi
    {
        try{
            return $apiInstance->getReport($reportId);
        }catch (\Exception $e){
            report($e);
            return false;
        }
    }

    public static function apiGetReport($config,$account,$reportType = null): bool|array
    {

        if(!$account)return false;
        $dateTime = date('Y-m-d H:i:s',(time() - 120));
        $query = CreateAndGetReport::query();
        if($reportType != 'null'){
            $query->where('report_type',$reportType);
        }

        $query->where('is_schedule',CreateAndGetReport::IS_SCHEDULE_NO); //0: 非计划报告、需要report_id
        $query->where('account_id',$account->id);
        $query->where('created_at','<=',$dateTime);
        $query->where(function ($query) {
            $query->whereNull('processing_status')
                ->orWhereNotIn('processing_status',CreateAndGetReport::$_processingStatus); //报告状态
        });
        $query->where('origin', 0);
        $query->orderBy('id','ASC');
        $needs = $query->get();
        if($needs->isEmpty())return true;

        $apiInstance = new ReportsApi($config);
        $re = [];
        foreach ($needs as $need){
            $report = self::_getReport($apiInstance,$need->report_id);
            if ($report) {
                $processingStatus = $report->getProcessingStatus();
                $re[$need->report_id] = $processingStatus;
                try{
                    DB::connection('mysql_amazon')->beginTransaction();
                    $need->report_type = $report->getReportType();
                    $need->data_start_time = gmdate('Y-m-d H:i:s',strtotime($report->getDataStartTime()));
                    $need->data_end_time = gmdate('Y-m-d H:i:s',strtotime($report->getDataEndTime()));
                    $need->created_time = gmdate('Y-m-d H:i:s',strtotime($report->getCreatedTime()));
                    $need->processing_status = $processingStatus;
                    if($processingStatus == 'DONE'){
                        $need->report_document_id = $report->getReportDocumentId();
                    }elseif($processingStatus == 'CANCELLED' || $processingStatus == 'FATAL'){
                        $need->is_parse = 1;//设置为已解析
                        $need->parse_desc = 3;//设置为无需解析
                    }
                    $need->save();
                    DB::connection('mysql_amazon')->commit();
                }catch (\Exception $e){
                    DB::connection('mysql_amazon')->rollBack();
                    report($e);
                }
            }
            sleep(2);
        }
        return $re;
    }

    /**
     * 下载报告并解析数据
     * @param $config
     * @param $account
     * @param $reportType
     * @return bool|array
     */
    public static function apiDownloadDocument($config,$account,$reportType): bool|array
    {
        ini_set('memory_limit', '512M');

        if(!$account || !$reportType)return false;
        $needs = CreateAndGetReport::query()
            ->where('account_id',$account->id)
            ->where('report_type',$reportType['name'])
            ->where('is_parse',0)
            ->where('processing_status','DONE')
            ->where('origin', 0)
            ->orderBy('id','ASC')
            ->get();
        if($needs->isEmpty())return true;

        //解析类（数据模型,表数据）(App\Logic\Amazon\Api\Parse\{GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE_V2})
        $parseClass = __NAMESPACE__."\\Parse\\".CreateAndGetReport::getParseClass($reportType['name']);

        //更新报告状态
        $apiInstance = new ReportsApi($config);
        $re = [];
        foreach ($needs as $need){
            $lines = self::_downloadDocument($apiInstance,$reportType,$need->report_document_id);
            $re[$need->report_id] = !$lines ? false : true;
            try{
                DB::connection('mysql_amazon')->beginTransaction();

                $param = [$lines,$account->id];
                //if ($reportType['name'] == 'GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE_V2') {
                //    $param[] = $need->id; //$need->report_type
                //}
                if($need->marketplace_id){
                    $param[] = AmazonCountry::getCountryIdByMarketplaceId($need->marketplace_id);
                }
                $parseStatus = call_user_func_array([$parseClass,'exe'],$param);
                $need->is_parse = 1; //解析状态 (0待解析 1已解析)
                $need->parse_desc = $parseStatus; //解析状况 (0未解析 1解析正常 2解析异常 3无需解析)
                $need->save();
                DB::connection('mysql_amazon')->commit();
            }catch (\Exception $e){
                DB::connection('mysql_amazon')->rollBack();
                report($e);
            }
            sleep(2);
        }
        return $re;
    }

    private static function _downloadDocument(ReportsApi $apiInstance,$reportType,$reportDocumentId){
        try{
            $reportDocumentInfo = $apiInstance->getReportDocument($reportDocumentId, $reportType);
            $docToDownload = new Document($reportDocumentInfo, $reportType);
            $docToDownload->download();// The raw report text
            return $docToDownload->getData();
        }catch (\Exception $e){
            report($e);
            return false;
        }
    }

    /**
     * 确认报告处理已完成(批量)(结算)
     * @param $config
     * @param $account
     * @param null $report_types [] A list of report types used to filter reports.
     * @param null $processing_statuses []
     * @param null $marketplace_ids []
     * @param int $page_size
     * @param null $created_since ISO 8601
     * @param null $created_until ISO 8601
     * @return bool
     */
    public static function apiGetReports($config,$account,$report_types, $processing_statuses = null, $marketplace_ids = null, $page_size = 10, $created_since = null, $created_until = null){
        if(!$account)return false;
        if(!$report_types)return false;

        $apiInstance = new ReportsApi($config);
        $reportsResponse = self::_getReports($apiInstance,$report_types, $processing_statuses, $marketplace_ids, $page_size, $created_since, $created_until);
        if($reportsResponse){
            try{
                $reports = $reportsResponse->getReports();
                if ($reports) {
                    DB::connection('mysql_amazon')->beginTransaction();
                    foreach ($reports as $report){
                        self::_extractReports($report,$account->id);
                        sleep(2);
                    }
                    $next_token = $reportsResponse->getNextToken();
                    self::_getReportsByNextToken($apiInstance,$account->id,$report_types, $processing_statuses, $marketplace_ids, $page_size, $created_since, $created_until,$next_token);

                    DB::connection('mysql_amazon')->commit();
                }
            }catch (\Exception $e){
                DB::connection('mysql_amazon')->rollBack();
                report($e);
                return false;
            }
        }

        return true;
    }

    private static function _getReportsByNextToken($apiInstance,$accountId,$report_types, $processing_statuses, $marketplace_ids, $page_size, $created_since, $created_until,$next_token){
        if($next_token){
            sleep(1);
            $reportsResponse = self::_getReports($apiInstance,$report_types, $processing_statuses, $marketplace_ids, $page_size, $created_since, $created_until,$next_token);
            if($reportsResponse){
                $reports = $reportsResponse->getReports();
                foreach ($reports as $report){
                    self::_extractReports($report,$accountId);
                }
                $next_token = $reportsResponse->getNextToken();
                self::_getReportsByNextToken($apiInstance,$accountId,$report_types, $processing_statuses, $marketplace_ids, $page_size, $created_since, $created_until,$next_token);
            }
        }
    }

    private static function _getReports(ReportsApi $apiInstance,$report_types, $processing_statuses, $marketplace_ids, $page_size, $created_since, $created_until,$next_token = null){
        try{

            $reportsResponse = $apiInstance->getReports($report_types, $processing_statuses, $marketplace_ids, $page_size, $created_since, $created_until, $next_token);
            return $reportsResponse;
        }catch (\Exception $e){
            report($e);
            return false;
        }
    }

    private static function _extractReports(ReportsModelApi $report,$accountId){
        try{
            DB::connection('mysql_amazon')->beginTransaction();
            $reportId = $report->getReportId();
            $processingStatus = $report->getProcessingStatus();
            $need = CreateAndGetReport::query()->where('report_id',$reportId)->first();
            if(!$need){
                $item = [
                    'account_id' => $accountId,
                    'report_type' => $report->getReportType(),
                    'report_id' => $reportId,
                    'data_start_time' => gmdate('Y-m-d H:i:s',strtotime($report->getDataStartTime())),
                    'data_end_time' => gmdate('Y-m-d H:i:s',strtotime($report->getDataEndTime())),
                    'created_time' => gmdate('Y-m-d H:i:s',strtotime($report->getCreatedTime())),
                    'processing_status' => $processingStatus,
                ];
                if($processingStatus == 'DONE'){
                    $item['report_document_id'] = $report->getReportDocumentId();
                }elseif($processingStatus == 'CANCELLED' || $processingStatus == 'FATAL'){
                    $item['is_parse'] = 1;//设置为已解析
                    $item['parse_desc'] = 3;//设置为无需解析
                }
                CreateAndGetReport::query()->create($item);
            }else{
                if($need->is_parse == 0){
                    $need->report_type = $report->getReportType();
                    $need->data_start_time = gmdate('Y-m-d H:i:s',strtotime($report->getDataStartTime()));
                    $need->data_end_time = gmdate('Y-m-d H:i:s',strtotime($report->getDataEndTime()));
                    $need->created_time = gmdate('Y-m-d H:i:s',strtotime($report->getCreatedTime()));
                    $need->processing_status = $processingStatus;
                    if($processingStatus == 'DONE'){
                        $need->report_document_id = $report->getReportDocumentId();
                    }elseif($processingStatus == 'CANCELLED' || $processingStatus == 'FATAL'){
                        $need->is_parse = 1;//设置为已解析
                        $need->parse_desc = 3;//设置为无需解析
                    }
                    $need->save();
                }
            }
            DB::connection('mysql_amazon')->commit();
        }catch (\Exception $e){
            DB::connection('mysql_amazon')->rollBack();
            report($e);
        }
    }

    /**
     * 创建计划报告
     * @param $config
     * @param $account
     * @param $data
     * @param $marketplace_ids
     * @param bool $multi
     * @return array|bool
     */
    public static function apiCreateReportSchedule($config,$account,$data,$marketplace_ids,$multi = false){
        if(!$account)return false;
        if(!isset($data['report_type']) || !$data['report_type'])return false;
        if(!isset($data['period']) || !$data['period'])return false;
        $apiInstance = new ReportsApi($config);
        $re = [];
        $count = count($marketplace_ids);
        DB::connection('mysql_amazon')->beginTransaction();
        try{
            $flag = false;
            if($multi){
                $s = 0;
                foreach ($marketplace_ids as $marketplaceId){
                    $data['marketplace_ids'] = [$marketplaceId];
                    $reportScheduleId = self::_createReportSchedule($apiInstance,$data);
                    self::_extractForReportScheduleIdByCreateReport($account->id,$reportScheduleId,$marketplaceId);
                    if($reportScheduleId)$s++;
                    $re[$marketplaceId]= $reportScheduleId;
                    sleep(2);
                }
                if($s == $count)$flag = true;
            }else{
                $data['marketplace_ids'] = $marketplace_ids;
                $reportScheduleId = self::_createReportSchedule($apiInstance,$data);
                self::_extractForReportScheduleIdByCreateReport($account->id,$reportScheduleId);
                $re[implode('-',$marketplace_ids)] = $reportScheduleId;
                sleep(2);
                if($reportScheduleId)$flag = true;
            }
            if($flag){
                DB::connection('mysql_amazon')->commit();
            }else{
                DB::connection('mysql_amazon')->rollBack();
            }
        }catch (\Exception $e){
            DB::connection('mysql_amazon')->rollBack();
            report($e);
        }
        return $re;
    }

    private static function _extractForReportScheduleIdByCreateReport($accountId,$reportScheduleId,$marketplaceId = null){
        if($reportScheduleId){
            $count = CreateAndGetReport::query()->where('report_schedule_id',$reportScheduleId)->count();
            if($count == 0){
                $res = [];
                $res['account_id'] = $accountId;
                $res['report_schedule_id'] = $reportScheduleId;
                $res['is_schedule'] = 1;
                $marketplaceId && $res['marketplace_id'] = $marketplaceId;
                CreateAndGetReport::query()->create($res);
            }
        }
    }

    private static function _createReportSchedule(ReportsApi $apiInstance,$data){
        $body = new CreateReportScheduleSpecification($data);
        try{
            $result = $apiInstance->createReportSchedule($body);
            return $result->getReportScheduleId();
        }catch (\Exception $e){
            report($e);
            return false;
        }
    }

}
